package org.sction.action.core;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.ParseException;
import java.util.Date;
import java.util.Enumeration;

import javax.servlet.ServletContext;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.commons.beanutils.ConvertUtils;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.shiro.SecurityUtils;
import org.sction.action.annotation.ActionParam;
import org.sction.action.annotation.MethodParam;
import org.sction.action.annotation.Parameter;
import org.sction.cache.CacheManager;
import org.sction.security.shiro.ShiroUser;
import org.sction.support.spring.SpringContextHolder;
import org.sction.util.ClassUtils;
import org.sction.util.DateUtils;
import org.sction.util.StringUtils;


/**
 * @author 作者 : sorc
 * @version 创建时间：2012-3-21 上午10:49:58
 * 
 *          类说明
 */
public class ActionUtils {

	private static final Logger logger = Logger.getLogger(ActionUtils.class);
	public static String objectFactory = "spring";
	public static String userSessionKey = "user";
	public static String authzAssertClass = null;
	public static String path = "";
	public static String realpath = "";
	// 默认日志级别
	public static Level logLevel = Level.OFF;

	public static String WORK_DIR = "";
	public static String FILE_UPLOAD_DIR = "";
	public static String DB_BACKUP_DIR = "";
	public static String SN_APPLY_ADDRESS = "";

	/**
	 * 按action路径取控制器类的缓存 key
	 * 
	 * @param path
	 *            例如: /oa/Msg!add.do or /oa/Msg.do?method=add
	 * @return 例如：/oa/Msg
	 */
	public static String pathControllerKey(String path) {
		int index = path.lastIndexOf("!");
		int doti = path.lastIndexOf(".");
		if (index != -1) {
			return path.substring(0, index);
		} else if (doti != -1) {
			return path.substring(0, doti);
		}
		return "";
	}

	public static String pathControllerKey(HttpServletRequest request) {
		String path = request.getServletPath();
		int index = path.lastIndexOf("!");
		int doti = path.lastIndexOf(".");
		if (index != -1) {
			return path.substring(0, index);
		} else if (doti != -1) {
			return path.substring(0, doti);
		}
		return "";
	}

	public static String pathActionKey(HttpServletRequest request) {
		String path = request.getServletPath();
		int index = path.lastIndexOf("!");
		int doti = path.lastIndexOf(".");
		String actionKey = request.getParameter("action");
		if (index != -1 && doti != -1) {
			actionKey = path.substring(index + 1, doti);
		}
		if (actionKey == null) {
			actionKey = "";
		}
		return actionKey;
	}

	/**
	 * 将request参数填充到bean
	 * 
	 * @param request
	 * @param values
	 * @param bean
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static <T> T parseRequest(ServletRequest request, Class<T> clazz,
			String[] values) {
		Enumeration<String> pnames = request.getParameterNames();
		Object bean;
		try {
			bean = clazz.newInstance();
			if (values != null && values.length > 0) {
				for (int i = 0; i < values.length; i++) {
					String string = values[i];
					try {
						Object beanValue = ConvertUtils.convert(
								request.getParameter(string),
								PropertyUtils.getPropertyType(bean, string));
						PropertyUtils.setProperty(bean, string, beanValue);
					} catch (IllegalAccessException e) {
						e.printStackTrace();
					} catch (InvocationTargetException e) {
						e.printStackTrace();
					} catch (NoSuchMethodException e) {
					}

				}
			} else {
				while (pnames.hasMoreElements()) {
					String pname = pnames.nextElement();
					try {
						if (request.getParameter(pname) != null) {
							Object beanValue = ConvertUtils.convert(
									request.getParameter(pname),
									PropertyUtils.getPropertyType(bean, pname));
							PropertyUtils.setProperty(bean, pname, beanValue);
						}
					} catch (IllegalAccessException e) {
						e.printStackTrace();
					} catch (InvocationTargetException e) {
						e.printStackTrace();
					} catch (NoSuchMethodException e) {
					}
				}
			}
			return (T) bean;
		} catch (InstantiationException e1) {
			e1.printStackTrace();
		} catch (IllegalAccessException e1) {
			e1.printStackTrace();
		}

		return null;
	}

	public static String getPath() {
		return path;
	}

	public static String getRealpath() {
		return realpath;
	}

	public static String printException(Throwable t, String templete, String hhf) {
		if (t == null) {
			return "";
		}
		String _hhf = hhf == null ? "\n" : hhf;
		String _templete = templete;
		if (templete == null) {
			_templete = "{title}{body}";
		}
		_templete = _templete.replace("{title}", t.getLocalizedMessage())
				+ _hhf;
		if (_templete.indexOf("{body}") != -1) {
			String body = "";
			StackTraceElement[] stes = t.getStackTrace();
			for (int i = 0; i < stes.length; i++) {
				StackTraceElement ste = stes[i];
				body += ste.getClassName() + "." + ste.getMethodName()
						+ "();on line " + ste.getLineNumber() + _hhf;

			}
			_templete = _templete.replace("{body}", body);
		}
		if (t.getCause() != null) {
			_templete += printException(t.getCause(), templete, hhf);
		}
		return _templete;
	}

	/**
	 * 初始化控制器参数
	 * 
	 * @param controller
	 * @param request
	 * @param response
	 */
	public static void initControllerParameters(Object controller,
			ServletRequest request, ServletResponse response,
			ServletContext servletContext) {
		Class<?> clazz = controller.getClass();
		Field[] fields = ClassUtils.allFields(clazz);
		for (Field field : fields) {
			if (field.getType().equals(HttpServletRequest.class)) {
				ClassUtils.invokeInjection(field, controller,
						((HttpServletRequest) request));
			} else if (field.getType().equals(HttpServletResponse.class)) {
				ClassUtils.invokeInjection(field, controller,
						((HttpServletResponse) response));
			} else if (field.getType().equals(HttpSession.class)) {
				ClassUtils.invokeInjection(field, controller,
						((HttpServletRequest) request).getSession());
			} else if (field.getType().equals(ServletRequest.class)) {
				ClassUtils.invokeInjection(field, controller, request);
			} else if (field.getType().equals(ServletResponse.class)) {
				ClassUtils.invokeInjection(field, controller, response);
			} else if (field.getType().equals(ServletContext.class)) {
				ClassUtils.invokeInjection(field, controller, servletContext);
			} else if (field.isAnnotationPresent(Parameter.class)) {
				Parameter paramName = field.getAnnotation(Parameter.class);
				String pName = paramName.value();
				if (pName.trim().equals("")) {
					pName = field.getName();
				}
				if (field.getType().equals(ShiroUser.class)) {
					ClassUtils.invokeInjection(field, controller,
							getShiroUser((HttpServletRequest) request));
				} else if (!ClassUtils.isBaseDataType(field.getType())) {
					Object p = ActionUtils.parseRequest(request,
							field.getType(), paramName.values());
					ClassUtils.invokeInjection(field, controller, p);
				} else if (field.getType().isArray()) {
					String[] pValues = request.getParameterValues(pName);
					try {
						if (paramName.overwrite()
								|| ClassUtils.invokeGet(field, controller) == null) {
							if (pValues != null && pValues.length > 0) {
								String cn = field.getType().getName()
										.replaceAll("\\[", "");
								if (cn.equals("L" + Integer.class.getName())) {
									Integer[] p = new Integer[pValues.length];
									for (int i = 0; i < pValues.length; i++) {
										p[i] = Integer.parseInt(pValues[i]
												.toString());
									}
									ClassUtils.invokeInjection(field,
											controller, p);
								} else if (cn.equals("I")) {
									int[] p = new int[pValues.length];
									for (int i = 0; i < pValues.length; i++) {
										p[i] = Integer.parseInt(pValues[i]
												.toString());
									}
									ClassUtils.invokeInjection(field,
											controller, p);
								} else if (cn.equals("L"
										+ Boolean.class.getName())) {
									Boolean[] p = new Boolean[pValues.length];
									for (int i = 0; i < pValues.length; i++) {
										p[i] = Boolean.parseBoolean(pValues[i]
												.toString());
									}
									ClassUtils.invokeInjection(field,
											controller, p);
								} else if (cn.equals("Z")) {
									boolean[] p = new boolean[pValues.length];
									for (int i = 0; i < pValues.length; i++) {
										p[i] = Boolean.parseBoolean(pValues[i]
												.toString());
									}
									ClassUtils.invokeInjection(field,
											controller, p);
								} else if (cn.equals("L"
										+ Float.class.getName())) {
									Float[] p = new Float[pValues.length];
									for (int i = 0; i < pValues.length; i++) {
										p[i] = Float.parseFloat(pValues[i]
												.toString());
									}
									ClassUtils.invokeInjection(field,
											controller, p);
								} else if (cn.equals("F")) {
									float[] p = new float[pValues.length];
									for (int i = 0; i < pValues.length; i++) {
										p[i] = Float.parseFloat(pValues[i]
												.toString());
									}
									ClassUtils.invokeInjection(field,
											controller, p);
								} else if (cn.equals("L"
										+ Double.class.getName())) {
									Double[] p = new Double[pValues.length];
									for (int i = 0; i < pValues.length; i++) {
										p[i] = Double.parseDouble(pValues[i]
												.toString());
									}
									ClassUtils.invokeInjection(field,
											controller, p);
								} else if (cn.equals("D")) {
									double[] p = new double[pValues.length];
									for (int i = 0; i < pValues.length; i++) {
										p[i] = Double.parseDouble(pValues[i]
												.toString());
									}
									ClassUtils.invokeInjection(field,
											controller, p);
								} else if (cn
										.equals("L" + Long.class.getName())) {
									Long[] p = new Long[pValues.length];
									for (int i = 0; i < pValues.length; i++) {
										p[i] = Long.parseLong(pValues[i]
												.toString());
									}
									ClassUtils.invokeInjection(field,
											controller, p);
								} else if (cn.equals("J")) {
									long[] p = new long[pValues.length];
									for (int i = 0; i < pValues.length; i++) {
										p[i] = Long.parseLong(pValues[i]
												.toString());
									}
									ClassUtils.invokeInjection(field,
											controller, p);
								} else if (cn
										.equals("L" + Date.class.getName())) {
									Date[] p = new Date[pValues.length];
									for (int i = 0; i < pValues.length; i++) {
										p[i] = DateUtils
												.StringAutoCast(pValues[i]
														.toString());
									}
									ClassUtils.invokeInjection(field,
											controller, p);
								} else if (cn.equals("L"
										+ java.sql.Date.class.getName())) {
									java.sql.Date[] p = new java.sql.Date[pValues.length];
									for (int i = 0; i < pValues.length; i++) {
										p[i] = new java.sql.Date(DateUtils
												.StringAutoCast(
														pValues[i].toString())
												.getTime());
									}
									ClassUtils.invokeInjection(field,
											controller, p);
								} else if (cn.equals("L"
										+ java.sql.Timestamp.class.getName())) {
									java.sql.Timestamp[] p = new java.sql.Timestamp[pValues.length];
									for (int i = 0; i < pValues.length; i++) {
										p[i] = new java.sql.Timestamp(DateUtils
												.StringAutoCast(
														pValues[i].toString())
												.getTime());
									}
									ClassUtils.invokeInjection(field,
											controller, p);

								} else {
									String[] p = new String[pValues.length];
									for (int i = 0; i < pValues.length; i++) {
										p[i] = (String) pValues[i];
									}
									ClassUtils.invokeInjection(field,
											controller, p);
								}
							}
						}
					} catch (IllegalArgumentException e) {
						logger.error("自动转换参数错误1!", e);
					} catch (ParseException e) {
						logger.error("自动转换参数错误3!数据转换错误", e);
					} catch (ClassCastException e) {
						logger.error("自动转换参数错误4!类转换错误", e);
					}
				} else {
					String pValue = request.getParameter(pName);
					if (pValue != null) {
						try {
							Object obj = StringUtils.StringCast(pValue,
									field.getType());
							if (paramName.overwrite()
									|| ClassUtils.invokeGet(field, controller) == null) {
								ClassUtils.invokeInjection(field, controller,
										obj);
							}
						} catch (IllegalArgumentException e) {
							logger.error("自动转换参数错误1!", e);
						} catch (ParseException e) {
							logger.error("自动转换参数错误3!数据转换错误", e);
						} catch (ClassCastException e) {
							logger.error("自动转换参数错误4!类转换错误", e);
						}
					}
				}
			}
		}
	}

	/**
	 * 从缓存中加载控制器
	 * 
	 * @param actionPath
	 * @return
	 */
	public static Object loadController(String actionPath) {
		Class<?> klazz = null;
		String controllerKey = null;
		Object obj = null;
		try {
			controllerKey = ActionUtils.pathControllerKey(actionPath);
			// 是否支持Spring
			if ("spring".equals(ActionUtils.objectFactory.toLowerCase().trim())) {
				obj = SpringContextHolder.getBean(controllerKey);
			}
			if (obj == null) {
				klazz = CacheManager.getController(controllerKey);
				if (klazz == null) {
					logger.error(controllerKey + " Controller not found");
					return null;
				}
				obj = klazz.newInstance();
			} else {
				klazz = obj.getClass();
			}
			// 对象注入是否支持Spring
			// if
			// ("spring".equals(ActionUtils.objectFactory.toLowerCase().trim()))
			// {
			// Field[] fields = ClassUtils.allFields(klazz);
			// for (Field field : fields) {
			// if (field.isAnnotationPresent(Resource.class)
			// || field.isAnnotationPresent(Autowired.class)) {
			// Object value = SpringContextHolder.getBean(field);
			// if (value != null) {
			// ClassUtils.invokeInjection(field, obj, value);
			// } else {
			// logger.warn("未找到属性 " + field.getName() + " 对应的值");
			// }
			// }
			// }
			// }
			return obj;
		} catch (Exception e) {
			logger.error(controllerKey + " Action error or not instance", e);
			return null;
		}
	}

	public static ShiroUser getShiroUser() {
		if ("shiro".equals(ActionUtils.authzAssertClass)) {
			ShiroUser user = new ShiroUser("", "");
			Object obj = SecurityUtils.getSubject().getPrincipal();
			if (obj != null) {
				if (obj instanceof ShiroUser) {
					user = (ShiroUser) obj;
				} else if (obj instanceof String) {
					user.setUserid(obj.toString());
					user.setLoginid(obj.toString());
					user.setUsername(obj.toString());
				}
				return user;
			}
		}
		return null;
	}

	public static ShiroUser getShiroUser(HttpServletRequest request) { 
		ShiroUser user = new ShiroUser("", "");
		Object obj = request.getSession().getAttribute(
				ActionUtils.userSessionKey);
		if (obj != null) {
			if (obj instanceof ShiroUser) {
				user = (ShiroUser) obj;
			} else if (obj instanceof String) {
				user.setUserid(obj.toString());
				user.setLoginid(obj.toString());
				user.setUsername(obj.toString());
			}
		}
		return user;
	}

	/**
	 * 获得Action方法的形参
	 * 
	 * @param action
	 * @param request
	 * @param response
	 * @return
	 */

	public static Object[] initActionParameters(Method action,
			ServletRequest request, ServletResponse response,
			ServletContext servletContext) {
		Class<?>[] paramTypes = action.getParameterTypes();
		Annotation[][] parameters = action.getParameterAnnotations();
		Object[] objs = new Object[paramTypes.length];
		for (int i = 0; i < paramTypes.length; i++) {
			Annotation[] parameter = parameters[i];
			Class<?> paramType = paramTypes[i];
			if (paramType.equals(HttpServletRequest.class)) {
				objs[i] = (HttpServletRequest) request;
			} else if (paramType.equals(HttpServletResponse.class)) {
				objs[i] = (HttpServletResponse) response;
			} else if (paramType.equals(HttpSession.class)) {
				objs[i] = ((HttpServletRequest) request).getSession();
			} else if (paramType.equals(ServletRequest.class)) {
				objs[i] = request;
			} else if (paramType.equals(ServletResponse.class)) {
				objs[i] = response;
			} else if (paramType.equals(ServletContext.class)) {
				objs[i] = servletContext;
			} else if (paramType.equals(ShiroUser.class)) {
				objs[i] = getShiroUser((HttpServletRequest) request);
			} else if (parameter.length > 0) {
				for (Annotation param : parameter) {
					String pName = "";
					if (param.annotationType().equals(ActionParam.class)) {
						ActionParam paramx = (ActionParam) param;
						pName = paramx.value();
					} else if (param.annotationType().equals(MethodParam.class)) {
						MethodParam paramx = (MethodParam) param;
						pName = paramx.value();
					}
					if (paramType.isArray()) {
						String[] pValues = request.getParameterValues(pName);
						try {
							if (pValues != null && pValues.length > 0) {
								String cn = paramType.getName().replaceAll(
										"\\[", "");
								if (cn.equals("L" + Integer.class.getName())) {
									Integer[] p = new Integer[pValues.length];
									for (int j = 0; j < pValues.length; j++) {
										p[j] = Integer.parseInt(pValues[j]
												.toString());
									}
									objs[i] = p;
								} else if (cn.equals("I")) {
									int[] p = new int[pValues.length];
									for (int j = 0; j < pValues.length; j++) {
										p[j] = Integer.parseInt(pValues[j]
												.toString());
									}
									objs[i] = p;
								} else if (cn.equals("L"
										+ Boolean.class.getName())) {
									Boolean[] p = new Boolean[pValues.length];
									for (int j = 0; j < pValues.length; j++) {
										p[j] = Boolean.parseBoolean(pValues[j]
												.toString());
									}
									objs[i] = p;
								} else if (cn.equals("Z")) {
									boolean[] p = new boolean[pValues.length];
									for (int j = 0; j < pValues.length; j++) {
										p[j] = Boolean.parseBoolean(pValues[j]
												.toString());
									}
									objs[i] = p;
								} else if (cn.equals("L"
										+ Float.class.getName())) {
									Float[] p = new Float[pValues.length];
									for (int j = 0; j < pValues.length; j++) {
										p[j] = Float.parseFloat(pValues[j]
												.toString());
									}
									objs[i] = p;
								} else if (cn.equals("F")) {
									float[] p = new float[pValues.length];
									for (int j = 0; j < pValues.length; j++) {
										p[j] = Float.parseFloat(pValues[j]
												.toString());
									}
									objs[i] = p;
								} else if (cn.equals("L"
										+ Double.class.getName())) {
									Double[] p = new Double[pValues.length];
									for (int j = 0; j < pValues.length; j++) {
										p[j] = Double.parseDouble(pValues[j]
												.toString());
									}
									objs[i] = p;
								} else if (cn.equals("D")) {
									double[] p = new double[pValues.length];
									for (int j = 0; j < pValues.length; j++) {
										p[j] = Double.parseDouble(pValues[j]
												.toString());
									}
									objs[i] = p;
								} else if (cn
										.equals("L" + Long.class.getName())) {
									Long[] p = new Long[pValues.length];
									for (int j = 0; j < pValues.length; j++) {
										p[j] = Long.parseLong(pValues[j]
												.toString());
									}
									objs[i] = p;
								} else if (cn.equals("J")) {
									long[] p = new long[pValues.length];
									for (int j = 0; j < pValues.length; j++) {
										p[j] = Long.parseLong(pValues[j]
												.toString());
									}
									objs[i] = p;
								} else if (cn
										.equals("L" + Date.class.getName())) {
									Date[] p = new Date[pValues.length];
									for (int j = 0; j < pValues.length; j++) {
										p[j] = DateUtils
												.StringAutoCast(pValues[j]
														.toString());
									}
									objs[i] = p;
								} else if (cn.equals("L"
										+ java.sql.Date.class.getName())) {
									java.sql.Date[] p = new java.sql.Date[pValues.length];
									for (int j = 0; j < pValues.length; j++) {
										p[j] = new java.sql.Date(DateUtils
												.StringAutoCast(
														pValues[j].toString())
												.getTime());
									}
									objs[i] = p;
								} else if (cn.equals("L"
										+ java.sql.Timestamp.class.getName())) {
									java.sql.Timestamp[] p = new java.sql.Timestamp[pValues.length];
									for (int j = 0; j < pValues.length; j++) {
										p[j] = new java.sql.Timestamp(DateUtils
												.StringAutoCast(
														pValues[j].toString())
												.getTime());
									}
									objs[i] = p;
								} else {
									String[] p = new String[pValues.length];
									for (int j = 0; j < pValues.length; j++) {
										p[j] = (String) pValues[j];
									}
									objs[i] = p;
								}
							}

						} catch (IllegalArgumentException e) {
							logger.error("自动转换Action参数错误1!误", e);
						} catch (ParseException e) {
							logger.error("自动转换Action参数错误3!数据转换错误", e);
						} catch (ClassCastException e) {
							logger.error("自动转换Action参数错误4!类转换错误", e);
						}
					} else {
						String pValue = request.getParameter(pName);
						if (pValue != null && objs[i] == null) {
							try {
								objs[i] = StringUtils.StringCast(pValue,
										paramType);
							} catch (IllegalArgumentException e) {
								logger.error("自动转换Action参数错误1!误", e);
							} catch (ParseException e) {
								logger.error("自动转换Action参数错误3!数据转换错误", e);
							} catch (ClassCastException e) {
								logger.error("自动转换Action参数错误4!类转换错误", e);
							}
						}
					}
					// else {
					// try {
					// objs[i] = paramType.newInstance();
					// Enumeration<String> params = request.getParameterNames();
					// while (params.hasMoreElements()) {
					// String paramName = params.nextElement();
					//
					// //Object obj = BeanUtils.getBeanProperty(objs[i],
					// paramName);
					//
					// Map map =
					// org.apache.commons.beanutils.BeanUtils.describe(objs[i]);
					// Set set = map.keySet();
					// for (Object object : set) {
					// }
					// // org.apache.commons.beanutils.BeanUtils
					// // .copyProperty(objs[i], paramName,
					// request.getParameter(paramName));
					// }
					//
					// } catch (Exception e) {
					//
					// }
					// }
				}
			}
		}
		return objs;
	}
}
